# -*- coding: utf-8 -*-
##############################################################################
#
#    OpenERP, Open Source Management Solution
#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Affero General Public License as
#    published by the Free Software Foundation, either version 3 of the
#    License, or (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Affero General Public License for more details.
#
#    You should have received a copy of the GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

import time

from openerp.osv import osv, fields
from openerp.tools.translate import _
from openerp import api
from openerp import fields as fields2
from openerp.tools import float_round, float_is_zero, float_compare
import openerp.addons.decimal_precision as dp

class account_invoice(osv.osv):

    _inherit = "account.invoice"
    
    @api.one
    def copy(self):
        raise osv.except_osv(_('Forbidden to duplicate'), _('Is not allowed to duplicate this invoice record.'))
    
    @api.multi
    def name_get(self):

        TYPES = {
            'out_invoice': _('Invoice'),
            'in_invoice': _('Supplier Invoice'),
            'out_refund': _('Refund'),
            'in_refund': _('Supplier Refund'),
        }
        result = []
        for inv in self:
            result.append((inv.id, "%s %s" % (inv.number or inv.internal_number or TYPES[inv.type], inv.name or '')))
        return result

    @api.model
    def name_search(self, name, args=None, operator='ilike', limit=100):

        args = args or []
        recs = self.browse()

        if name:
            recs = self.search([('number', '=', name)] + args, limit=limit)
        if not recs:
            recs = self.search([('name', operator, name)] + args, limit=limit)
        if not recs:
            recs = self.search([('internal_number', operator, name)] + args, limit=limit)
        return recs.name_get()


    def _rate(self, cr, uid, ids, name, arg, context=None):
        if not ids: return {}
        res = {}
        res_currency_rate_obj = self.pool.get("res.currency.rate")
        res_users_obj = self.pool.get("res.users")
        home_currecy_id = res_users_obj.browse(cr, uid, uid, context=context).company_id.currency_id.id
         
        for obj in self.browse(cr, uid, ids, context=context):
            cur_id = obj.currency_id and obj.currency_id.id or False
            cur_date = obj.date_invoice or fields.date.context_today(self, cr, uid, context=context)
            if obj.change_rate and cur_id != home_currecy_id:
                res[obj.id] = obj.change_rate
            else:
                if cur_id and cur_date:
                    res_currency_rate_ids = res_currency_rate_obj.search(cr, uid, [('currency_id', '=', cur_id), ('name', '<=', cur_date)], order='name DESC', limit=1)
                    if res_currency_rate_ids:
                        res[obj.id] = res_currency_rate_obj.browse(cr, uid, res_currency_rate_ids[0], context=context).rate
                    else:
                        res[obj.id] = 0
                else:
                    res[obj.id] = 0
 
        return res

    def _get_conversion_rate(self, cr, uid, from_currency, to_currency, context=None):
        if context is None:
            context = {}
        ctx = context.copy()
        cur_obj = self.pool.get('res.currency')
        ctx.update({'currency_rate_type_id': ctx.get('currency_rate_type_from')})
        from_currency = cur_obj.browse(cr, uid, from_currency.id, context=ctx)
        ctx.update({'currency_rate_type_id': ctx.get('currency_rate_type_to')})
        to_currency = cur_obj.browse(cr, uid, to_currency.id, context=ctx)
        if from_currency.rate == 0 or to_currency.rate == 0:
            date = context.get('date', time.strftime('%Y-%m-%d'))
            if from_currency.rate == 0:
                currency_symbol = from_currency.symbol
            else:
                currency_symbol = to_currency.symbol
            raise osv.except_osv(_('Error'), _('No rate found \n' \
                    'for the currency: %s \n' \
                    'at the date: %s') % (currency_symbol, date))
        rate = context.get('change_rate', 0)
        return to_currency.rate/((rate > 0 and rate) or from_currency.rate)
# 
    def compute_curr(self, cr, uid, ids, from_currency_id, to_currency_id, from_amount,
                round=True, currency_rate_type_from=False, currency_rate_type_to=False, context=None):
        if not context:
            context = {}
        ctx = context.copy()
        cur_obj = self.pool.get('res.currency')
        if not from_currency_id:
            from_currency_id = to_currency_id
        if not to_currency_id:
            to_currency_id = from_currency_id
        
        xc = cur_obj.browse(cr, uid, [from_currency_id,to_currency_id], context=context)
        from_currency = (xc[0].id == from_currency_id and xc[0]) or xc[1]
        to_currency = (xc[0].id == to_currency_id and xc[0]) or xc[1]

        if (to_currency_id == from_currency_id) and (currency_rate_type_from == currency_rate_type_to):
            if round:
                return float_round(from_amount, precision_rounding=to_currency.rounding)
            else:
                return from_amount
        else:
            ctx.update({'currency_rate_type_from': currency_rate_type_from, 'currency_rate_type_to': currency_rate_type_to})
 
             
            rate = self._get_conversion_rate(cr, uid, from_currency, to_currency, context=ctx)

            ttl = from_amount * rate
            if round:
                return float_round(ttl, precision_rounding=to_currency.rounding)
            else:
                return ttl


    @api.multi
    def compute_invoice_totals(self, company_currency, ref, invoice_move_lines):
        total = 0
        total_currency = 0
        context = {}
        for line in invoice_move_lines:
            if self.currency_id != company_currency:

                context.update({'date': self.date_invoice or fields2.Date.context_today(self)})
                context.update({'change_rate': self.change_rate})
                 
                currency = self.currency_id.with_context(date=self.date_invoice or fields.Date.context_today(self))
                line['currency_id'] = currency.id
                line['amount_currency'] = line['price']
                line['exrate'] = self.cur_rate

                line['price'] = self.with_context(context).compute_curr(self.currency_id.id, self.company_id.currency_id.id, line['price'])
            else:
                line['currency_id'] = False
                line['amount_currency'] = False
                line['exrate'] = False
            line['ref'] = ref
            if self.type in ('out_invoice','in_refund'):
                total += line['price']
                total_currency += line['amount_currency'] or line['price']
                line['price'] = - line['price']
            else:
                total -= line['price']
                total_currency -= line['amount_currency'] or line['price']
        return total, total_currency, invoice_move_lines
# 
    @api.model
    def line_get_convert(self, line, part, date, is_refund):
        
        return {
            'date_maturity': line.get('date_maturity', False),
            'partner_id': part,
            'name': line['name'][:64],
            'date': date,
            'debit': line['price']>0 and line['price'],
            'credit': line['price']<0 and -line['price'],
            'account_id': line['account_id'],
            'analytic_lines': line.get('analytic_lines', []),
            'amount_currency': line['price']>0 and abs(line.get('amount_currency', False)) or -abs(line.get('amount_currency', False)),
            'currency_id': line.get('currency_id', False),
            'tax_code_id': line.get('tax_code_id', False),
            'tax_amount': line.get('tax_amount', False),
            'ref': line.get('ref', False),
            'quantity': line.get('quantity',1.00),
            'product_id': line.get('product_id', False),
            'product_uom_id': line.get('uos_id', False),
            'analytic_account_id': line.get('account_analytic_id', False),
            'exrate':  line.get('exrate', False),
            'is_refund': is_refund,
        }
 
    @api.multi
    def action_move_create(self):
        """ Creates invoice related analytics and financial move lines """
        account_invoice_tax = self.env['account.invoice.tax']
        account_move = self.env['account.move']
 
        for inv in self:
            if not inv.journal_id.sequence_id:
                raise except_orm(_('Error!'), _('Please define sequence on the journal related to this invoice.'))
            if not inv.invoice_line:
                raise except_orm(_('No Invoice Lines!'), _('Please create some invoice lines.'))
            if inv.move_id:
                continue
 
            ctx = dict(self._context, lang=inv.partner_id.lang)
            if not inv.date_invoice:
                inv.with_context(ctx).write({'date_invoice': fields2.Date.context_today(self)})
            date_invoice = inv.date_invoice
 
            company_currency = inv.company_id.currency_id
            # create the analytical lines, one move line per invoice line
            iml = inv._get_analytic_lines()
            # check if taxes are all computed
            compute_taxes = account_invoice_tax.compute(inv)
            inv.check_tax_lines(compute_taxes)
 
            # I disabled the check_total feature
            if self.env['res.users'].has_group('account.group_supplier_inv_check_total'):
                if inv.type in ('in_invoice', 'in_refund') and abs(inv.check_total - inv.amount_total) >= (inv.currency_id.rounding / 2.0):
                    raise except_orm(_('Bad Total!'), _('Please verify the price of the invoice!\nThe encoded total does not match the computed total.'))
 
            if inv.payment_term:
                total_fixed = total_percent = 0
                for line in inv.payment_term.line_ids:
                    if line.value == 'fixed':
                        total_fixed += line.value_amount
                    if line.value == 'procent':
                        total_percent += line.value_amount
                total_fixed = (total_fixed * 100) / (inv.amount_total or 1.0)
                if (total_fixed + total_percent) > 100:
                    raise except_orm(_('Error!'), _("Cannot create the invoice.\nThe related payment term is probably misconfigured as it gives a computed amount greater than the total invoiced amount. In order to avoid rounding issues, the latest line of your payment term must be of type 'balance'."))
 
            # one move line per tax line
            iml += account_invoice_tax.move_line_get(inv.id)
 
 
            is_refund = False
            if inv.type in ('out_refund', 'in_refund'):
                is_refund = True
            if inv.type in ('in_invoice', 'in_refund'):
                ref = inv.reference
            else:
                ref = inv.number
 
            diff_currency = inv.currency_id != company_currency
            # create one move line for the total and possibly adjust the other lines amount
            total, total_currency, iml = inv.with_context(ctx).compute_invoice_totals(company_currency, ref, iml)
 
            name = inv.name or inv.supplier_invoice_number or '/'
            totlines = []
            if inv.payment_term:
                totlines = inv.with_context(ctx).payment_term.compute(total, date_invoice)[0]
            if totlines:
                res_amount_currency = total_currency
                ctx['date'] = date_invoice
                ctx['change_rate'] = inv.change_rate
                for i, t in enumerate(totlines):
                    if inv.currency_id != company_currency:
                        amount_currency = self.with_context(ctx).compute_curr(self.company_id.currency_id.id, self.currency_id.id, t[1])
                        exrate = inv.cur_rate
                    else:
                        amount_currency = False
                        exrate = False
 
                    # last line: add the diff
                    res_amount_currency -= amount_currency or 0
                    if i + 1 == len(totlines):
                        amount_currency += res_amount_currency
 
                    iml.append({
                        'type': 'dest',
                        'name': name,
                        'price': t[1],
                        'account_id': inv.account_id.id,
                        'date_maturity': t[0],
                        'amount_currency': diff_currency and amount_currency,
                        'currency_id': diff_currency and inv.currency_id.id,
                        'exrate': diff_currency and inv.cur_rate or False,
                        'ref': ref,
                    })
            else:
                iml.append({
                    'type': 'dest',
                    'name': name,
                    'price': total,
                    'account_id': inv.account_id.id,
                    'date_maturity': inv.date_due,
                    'amount_currency': diff_currency and total_currency,
                    'currency_id': diff_currency and inv.currency_id.id,
                    'exrate': diff_currency and inv.cur_rate or False,
                    'ref': ref
                })
 
 
            date = inv.date_invoice or time.strftime('%Y-%m-%d')
             
            part = self.env['res.partner']._find_accounting_partner(inv.partner_id)
 
            line = [(0, 0, self.line_get_convert(l, part.id, date, is_refund)) for l in iml]
            line = inv.group_lines(iml, line)
 
            journal = inv.journal_id.with_context(ctx)
            if journal.centralisation:
                raise except_orm(_('User Error!'),
                        _('You cannot create an invoice on a centralized journal. Uncheck the centralized counterpart box in the related journal from the configuration menu.'))
 
            line = inv.finalize_invoice_move_lines(line)
 
            move_vals = {
                'ref': inv.reference or inv.name,
                'line_id': line,
                'journal_id': journal.id,
                'date': date,
                'narration': inv.comment,
                'company_id': inv.company_id.id,
            }
            ctx['company_id'] = inv.company_id.id
            period = inv.period_id
            if not period:
                period = period.with_context(ctx).find(date_invoice)[:1]
            if period:
                move_vals['period_id'] = period.id
                for i in line:
                    i[2]['period_id'] = period.id
 
            ctx['invoice'] = inv
            move = account_move.with_context(ctx).create(move_vals)
            # make the invoice point to that move
            vals = {
                'move_id': move.id,
                'period_id': period.id,
                'move_name': move.name,
            }
            inv.with_context(ctx).write(vals)
            # Pass invoice in context in method post: used if you want to get the same
            # account move reference when creating the same invoice after a cancelled one:
            move.post()
        self._log_event()
        return True

    def _get_picking_id(self, cr, uid, ids, name, args, context=None):
        ail_obj = self.pool.get("account.invoice.line")
        res = {}
        for invoice in self.browse(cr, uid, ids, context=context):
            res[invoice.id] = False
            
            if invoice.invoice_line:
                invoice_line_ids = ail_obj.search(cr, uid, [('invoice_id', '=', invoice.id)], context=context)
                if invoice_line_ids:
                    ail = ail_obj.browse(cr, uid, invoice_line_ids[0], context=context)
                    res[invoice.id] = (ail.stock_move_id and ail.stock_move_id.picking_id.id) or False
        return res

    def _get_client_order_ref(self, cr, uid, ids, name, args, context=None):
        res = {}
        for invoice in self.browse(cr, uid, ids, context=context):
            res[invoice.id] = ''
            
            if invoice.sale_ids:
                for so in invoice.sale_ids:
                    res[invoice.id] = so.client_order_ref
            elif invoice.picking_id:
                res[invoice.id] = (invoice.picking_id.sale_id and invoice.picking_id.sale_id.client_order_ref) or ''
        return res

    def _get_sale_id(self, cr, uid, ids, name, args, context=None):
        ail_obj = self.pool.get("account.invoice.line")
        res = {}
        for invoice in self.browse(cr, uid, ids, context=context):
            res[invoice.id] = False
            if invoice.sale_ids:
                for so in invoice.sale_ids:
                    res[invoice.id] = so.id
            elif invoice.picking_id:
                res[invoice.id] = (invoice.picking_id.sale_id and invoice.picking_id.sale_id.id) or False

        return res

    def _amount_all_home(self, cr, uid, ids, name, args, context=None):
        res = {}
        for invoice in self.browse(cr, uid, ids, context=context):
            change_rate = invoice.change_rate
            cur_rate = invoice.cur_rate
            res[invoice.id] = {
                'amount_untaxed_home': 0.0,
                'amount_tax_home': 0.0,
                'amount_total_home': 0.0
            }
            for line in invoice.invoice_line:
                res[invoice.id]['amount_untaxed_home'] += (line.price_subtotal)
            for line in invoice.tax_line:
                res[invoice.id]['amount_tax_home'] += line.amount
            
            invoice_rate = ((change_rate > 0) and change_rate) or cur_rate
            company_currency_rate = invoice.company_id.currency_id.rate
            if invoice.company_id.currency_id.id != invoice.currency_id.id:
                res[invoice.id]['amount_tax_home'] = float_round(res[invoice.id]['amount_tax_home'] / invoice_rate * company_currency_rate,2)
                res[invoice.id]['amount_untaxed_home'] = float_round(res[invoice.id]['amount_untaxed_home'] / invoice_rate * company_currency_rate,2)

            res[invoice.id]['amount_total_home'] = res[invoice.id]['amount_tax_home'] + res[invoice.id]['amount_untaxed_home']
        return res

#     def _compute_residual(self):
#         self.residual = 0.0
#         # Each partial reconciliation is considered only once for each invoice it appears into,
#         # and its residual amount is divided by this number of invoices
#         partial_reconciliations_done = []
#         for line in self.sudo().move_id.line_id:
#             print 'y'
#             print 'a'
#             print 'n'
#             print 't'
#             print 'o'
#             if line.account_id.type not in ('receivable', 'payable'):
#                 continue
#             if line.reconcile_partial_id and line.reconcile_partial_id.id in partial_reconciliations_done:
#                 continue
#             # Get the correct line residual amount
#             if line.currency_id == self.currency_id:
#                 line_amount = line.currency_id and line.amount_residual_currency or line.amount_residual
#                 print 'o'
#                 print line_amount
#             else:
#                 
#                 from_currency = line.company_id.currency_id.with_context(date=line.date)
#                 print line.amount_residual
#                 line_amount = from_currency.compute(line.amount_residual, self.currency_id)
#                 print 'y'
#                 print line_amount
#             raise osv.except_osv(_('Error'), _('No rate found \n' \
#                     'for the currency: %s \n' \
#                     'at the date: %s') % ('tt', 'xx'))
#             print line_amount
#             # For partially reconciled lines, split the residual amount
#             if line.reconcile_partial_id:
#                 partial_reconciliation_invoices = set()
#                 for pline in line.reconcile_partial_id.line_partial_ids:
#                     if pline.invoice and self.type == pline.invoice.type:
#                         partial_reconciliation_invoices.update([pline.invoice.id])
#                 line_amount = self.currency_id.round(line_amount / len(partial_reconciliation_invoices))
#                 partial_reconciliations_done.append(line.reconcile_partial_id.id)
#             self.residual += line_amount
#         print self.residual
#         self.residual = max(self.residual, 0.0)

    _columns = {
        'amount_untaxed_home': fields.function(_amount_all_home, digits_compute=dp.get_precision('Account'), string='Untaxed Home',
            multi='all'),
        'amount_tax_home': fields.function(_amount_all_home, digits_compute=dp.get_precision('Account'), string='Tax Home',
            multi='all'),
        'amount_total_home': fields.function(_amount_all_home, digits_compute=dp.get_precision('Account'), string='Total Home',
            multi='all'),
        'sale_id': fields.function(_get_sale_id, type="many2one", relation="sale.order", string="Sale Order"),
        'sale_ids': fields.many2many('account.invoice', 'sale_order_invoice_rel', 'invoice_id', 'order_id', 'Sales', readonly=True, copy=False),
        'client_order_ref': fields.function(_get_client_order_ref, type="char", string="Reference/Description"),
        'picking_id': fields.function(_get_picking_id, type="many2one", relation="stock.picking", string="Stock Picking"),
        'change_rate': fields.float('Invoice Exchange Rate', digits=(12,6), help='Change Currency Rate', readonly=True, states={'draft':[('readonly',False)]}),
        'cur_rate': fields.function(_rate, type='float', digits=(12,6), string='Current Currency Rate', readonly=True, store=True),
        }

class account_invoice_refund(osv.osv_memory):
 
    _inherit = "account.invoice.refund"
 
    _columns = {
        'filter_refund': fields.selection([('refund', 'Create a draft refund')], "Refund Method", required=True, help='Refund base on this type. You can not Modify and Cancel if the invoice is already reconciled'),
     }
 
account_invoice_refund()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
